using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate;

using HadronData;

namespace HadronEditor
{
    public enum EditTool
    {
        LayerMove,
        LayerRotate,
        EdgeDraw,
        EdgeDelete,
        EdgeColour,
        VertexMove,
        EntityNew,
        EntityProperties,
        EntityMove,
        EntityRotate,
        EntityDelete
    }

    public partial class MainForm : Form
    {
        private World m_world;
        private int m_nSelectedLevel, m_nSelectedLayer;
        private EditTool m_editTool;
        private Vector2 m_vecMousePosition;
        private bool m_bDragging;
        private Vector2 m_vecDragStart, m_vecDragEnd;
        private Vector2 m_vecViewportPosition;
        private System.Drawing.Color m_colorEdge;
        private Vector2 m_vecOldPosition;
        private float m_fOldOrientation;
        private List<PickedVertex> m_listPickedVertices;
        private List<PickedEdge> m_listPickedEdges;
        private EntityMarker m_entityMarkerSelected;

        private Bitmap CreateColourBitmap(System.Drawing.Color p_colour)
        {
            Bitmap bitmap = new Bitmap(16, 16);
            for (int nIndex = 0; nIndex < 16; nIndex++)
            {
                bitmap.SetPixel(nIndex, 0, System.Drawing.Color.Black);
                bitmap.SetPixel(nIndex, 15, System.Drawing.Color.Black);
                bitmap.SetPixel(0, nIndex, System.Drawing.Color.Black);
                bitmap.SetPixel(15, nIndex, System.Drawing.Color.Black);
            }
            for (int nY = 1; nY < 15; nY++)
                for (int nX = 1; nX < 15; nX++)
                    bitmap.SetPixel(nX, nY, p_colour);

            System.Drawing.Color colourLight = System.Drawing.Color.FromArgb(
                Math.Min(p_colour.R + 64, 255),
                Math.Min(p_colour.G + 64, 255),
                Math.Min(p_colour.B + 64, 255));
            for (int nIndex = 1; nIndex < 14; nIndex++)
            {
                bitmap.SetPixel(nIndex, 1, colourLight);
                bitmap.SetPixel(1, nIndex, colourLight);
            }

            System.Drawing.Color colourDark = System.Drawing.Color.FromArgb(
                Math.Max(p_colour.R - 64, 0),
                Math.Max(p_colour.G - 64, 0),
                Math.Max(p_colour.B - 64, 0));
            for (int nIndex = 2; nIndex < 15; nIndex++)
            {
                bitmap.SetPixel(nIndex, 14, colourDark);
                bitmap.SetPixel(14, nIndex, colourDark);
            }

            return bitmap;
        }

        private void DrawOrigin(Graphics p_graphics, Transformation p_trnPlacement, System.Drawing.Color p_colour)
        {
            Pen pen = new Pen(p_colour);
            Vector2 vecPoint1 = p_trnPlacement.Translation - p_trnPlacement.RotationDirection * 20.0f;
            Vector2 vecPoint2 = p_trnPlacement.Translation + p_trnPlacement.RotationDirection * 20.0f;
            p_graphics.DrawLine(pen, vecPoint1.X, vecPoint1.Y, vecPoint2.X, vecPoint2.Y);
            Transformation trnPerpendicular = p_trnPlacement;
            trnPerpendicular.Rotation += MathHelper.PiOver2;
            vecPoint1 = p_trnPlacement.Translation - trnPerpendicular.RotationDirection * 20.0f;
            vecPoint2 = p_trnPlacement.Translation + trnPerpendicular.RotationDirection * 20.0f;
            p_graphics.DrawLine(pen, vecPoint1.X, vecPoint1.Y, vecPoint2.X, vecPoint2.Y);
            p_graphics.DrawEllipse(pen, p_trnPlacement.Translation.X - 10.0f, p_trnPlacement.Translation.Y - 10.0f, 20.0f, 20.0f);
            p_graphics.FillEllipse(new SolidBrush(p_colour), vecPoint2.X - 2.0f, vecPoint2.Y - 2.0f, 4.0f, 4.0f);
        }

        private void PrintText(Graphics p_graphics, String p_strText, Vector2 p_vecPosition, System.Drawing.Color p_colour)
        {
            p_graphics.DrawString(p_strText, this.Font, Brushes.Black, p_vecPosition.X - 1.0f, p_vecPosition.Y);
            p_graphics.DrawString(p_strText, this.Font, Brushes.Black, p_vecPosition.X + 1.0f, p_vecPosition.Y);
            p_graphics.DrawString(p_strText, this.Font, Brushes.Black, p_vecPosition.X, p_vecPosition.Y - 1.0f);
            p_graphics.DrawString(p_strText, this.Font, Brushes.Black, p_vecPosition.X, p_vecPosition.Y + 1.0f);
            p_graphics.DrawString(p_strText, this.Font, new SolidBrush(p_colour), p_vecPosition.X, p_vecPosition.Y);
        }

        private Microsoft.Xna.Framework.Graphics.Color ConvertGDIToXNAColour(System.Drawing.Color p_colourGDI)
        {
            return new Microsoft.Xna.Framework.Graphics.Color(p_colourGDI.R, p_colourGDI.G, p_colourGDI.B);
        }

        private System.Drawing.Color ConvertXNAToGDIColour(Microsoft.Xna.Framework.Graphics.Color p_colourXNA)
        {
            return System.Drawing.Color.FromArgb(p_colourXNA.R, p_colourXNA.G, p_colourXNA.B);
        }

        private void UpdateUILayerList()
        {
            m_cmbLayer.Items.Clear();

            if (m_nSelectedLevel < 0)
                return;

            Level level = m_world.Levels[m_nSelectedLevel];

            foreach (Layer layer in level.Layers)
                m_cmbLayer.Items.Add(layer.Name);
            
            m_cmbLayer.SelectedIndex = m_nSelectedLayer;
        }

        private void UpdateUILevelList()
        {
            m_cmbLevel.Items.Clear();
            foreach (Level level in m_world.Levels)
                m_cmbLevel.Items.Add(level.Name);
            m_cmbLevel.SelectedIndex = m_nSelectedLevel;
            m_nSelectedLayer = m_world.Levels[m_nSelectedLevel].Layers.Count > 0 ? 0 : -1;
        }

        private void UpdateUIButtonState()
        {
            bool bLevelSelected = m_nSelectedLevel >= 0;
            bool bLayerSelected = m_nSelectedLayer >= 0;

            m_btnLevelProperties.Enabled = bLevelSelected;
            m_btnLevelDelete.Enabled = bLevelSelected;

            m_cmbLayer.Enabled = bLevelSelected;
            m_btnLayerNew.Enabled = bLevelSelected;
            m_btnLayerProperties.Enabled = bLayerSelected;
            m_btnLayerMove.Enabled = bLayerSelected;
            m_btnLayerRotate.Enabled = bLayerSelected;
            m_btnLayerDelete.Enabled = bLayerSelected;

            m_btnDrawDrawEdge.Enabled = bLayerSelected;
            m_btnDrawDragVertex.Enabled = bLayerSelected;
            m_btnDrawDeleteEdge.Enabled = bLayerSelected && m_world.Levels[m_nSelectedLevel].Layers[m_nSelectedLayer].Edges.Count > 0;
            m_btnDrawColourEdge.Enabled = bLayerSelected;

            m_lblEntityChoose.Enabled = bLevelSelected;
            m_cmbEntity.Enabled = bLevelSelected;
            m_btnEntityNew.Enabled = bLevelSelected;
            m_btnEntityProperties.Enabled = bLevelSelected;
            m_btnEntityMove.Enabled = bLevelSelected;
            m_btnEntityRotate.Enabled = bLevelSelected;
            m_btnEntityDelete.Enabled = bLevelSelected;

            m_btnLayerMove.Checked = m_editTool == EditTool.LayerMove;
            m_btnLayerRotate.Checked = m_editTool == EditTool.LayerRotate;
            m_btnDrawDrawEdge.Checked = m_editTool == EditTool.EdgeDraw;
            m_btnDrawDragVertex.Checked = m_editTool == EditTool.VertexMove;
            m_btnDrawDeleteEdge.Checked = m_editTool == EditTool.EdgeDelete;
            m_btnDrawColourEdge.Checked = m_editTool == EditTool.EdgeColour;
            m_btnEntityNew.Checked = m_editTool == EditTool.EntityNew;
            m_btnEntityProperties.Checked = m_editTool == EditTool.EntityProperties;
            m_btnEntityMove.Checked = m_editTool == EditTool.EntityMove;
            m_btnEntityRotate.Checked = m_editTool == EditTool.EntityRotate;
            m_btnEntityDelete.Checked = m_editTool == EditTool.EntityDelete;
        }

        private void UpdateUIScrollBars()
        {
            if (m_nSelectedLevel < 0)
                return;

            m_hsbMain.Maximum = (int)m_world.Levels[m_nSelectedLevel].Dimensions.X;
            m_hsbMain.LargeChange = this.m_tscMain.ClientSize.Width;

            m_vsbMain.Maximum = (int)m_world.Levels[m_nSelectedLevel].Dimensions.Y;
            m_vsbMain.LargeChange = this.m_tscMain.ClientSize.Height;
        }

        private void UpdateUI()
        {
            UpdateUILevelList();
            UpdateUILayerList();
            UpdateUIButtonState();
            UpdateUIScrollBars();

            m_tscMain.ContentPanel.Invalidate();
        }

        private void ResetToolStrips()
        {
            m_tscMain.TopToolStripPanel.Controls.Clear();
            m_tlsFile.Location = new System.Drawing.Point(0, 0);
            m_tscMain.TopToolStripPanel.Controls.Add(m_tlsFile);
            m_tlsLevel.Location = new System.Drawing.Point(m_tlsFile.Right + 1, 0);
            m_tscMain.TopToolStripPanel.Controls.Add(m_tlsLevel);
            m_tlsLayer.Location = new System.Drawing.Point(m_tlsLevel.Right + 1, 0);
            m_tscMain.TopToolStripPanel.Controls.Add(m_tlsLayer);
            m_tlsDraw.Location = new System.Drawing.Point(m_tlsLayer.Right + 1, 0);
            m_tscMain.TopToolStripPanel.Controls.Add(m_tlsDraw);
            m_tlsEntity.Location = new System.Drawing.Point(m_tlsDraw.Right + 1, 0);
            m_tscMain.TopToolStripPanel.Controls.Add(m_tlsEntity);
        }

        private void MainForm_Load(object p_objSender, EventArgs p_eventArgs)
        {
            m_world = new World();
            Layer layer = new Layer();
            Level level = new Level();
            level.Name = NameManager.GenerateLevelName(m_world);
            layer.Name = NameManager.GenerateLayerName(level);
            level.Layers.Add(layer);
            m_world.Levels.Add(level);
            m_nSelectedLevel = 0;
            m_nSelectedLayer = 0;
            m_editTool = EditTool.EdgeDraw;
            m_colorEdge = System.Drawing.Color.Red;
            m_vecViewportPosition = Vector2.Zero;
            m_listPickedVertices = new List<PickedVertex>();
            m_listPickedEdges = new List<PickedEdge>();
            m_entityMarkerSelected = null;

            m_btnDrawChooseColour.Image = CreateColourBitmap(m_colorEdge);

            // populate entity type
            foreach (EntityType entityType in Enum.GetValues(typeof (EntityType)))
                m_cmbEntity.Items.Add(entityType.ToString());
            m_cmbEntity.SelectedIndex = 0;

            UpdateUI();
            ResetToolStrips();
            m_tscMain.ContentPanel.Cursor = Cursors.Cross;
        }

        private void MainForm_Resize(object p_objSender, EventArgs p_eventArgs)
        {
            UpdateUIScrollBars();
        }

        private void m_btnFileNew_Click(object p_objSender, EventArgs p_eventArgs)
        {
            if (MessageBox.Show(
                "Discard the existing world and create a new one?", "New World",
                MessageBoxButtons.YesNo, MessageBoxIcon.Question,
                MessageBoxDefaultButton.Button2) == DialogResult.Yes)
            {
                Level level = new Level();
                level.Name = NameManager.GenerateLevelName(m_world);
                Layer layer = new Layer();
                layer.Name = NameManager.GenerateLayerName(level);
                level.Layers.Add(layer);
                m_world = new World();
                m_world.Levels.Add(level);
                m_nSelectedLevel = 0;
                m_nSelectedLayer = 0;

                UpdateUI();
            }
        }

        private void m_btnFileOpen_Click(object p_objSender, EventArgs p_eventArgs)
        {
            if (m_openFileDialog.ShowDialog(this) == DialogResult.OK)
            {
                XmlReader xmlReader = XmlReader.Create(m_openFileDialog.FileName);
                m_world.Levels.Clear();
                m_world = IntermediateSerializer.Deserialize<World>(xmlReader, m_openFileDialog.FileName);
                xmlReader.Close();
                if (m_world.Levels.Count > 0)
                {
                    m_nSelectedLevel = 0;
                    m_nSelectedLayer = m_world.Levels[0].Layers.Count > 0 ? 0 : -1;
                }
                else
                    m_nSelectedLevel = m_nSelectedLayer = -1;
                UpdateUI();
            }
        }

        private void m_btnFileSave_Click(object p_objSender, EventArgs p_eventArgs)
        {
            if (m_saveFileDialog.ShowDialog(this) == DialogResult.OK)
            {
                XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
                xmlWriterSettings.Indent = true;
                XmlWriter xmlWriter = XmlWriter.Create(m_saveFileDialog.FileName, xmlWriterSettings);
                IntermediateSerializer.Serialize<World>(xmlWriter, m_world, m_saveFileDialog.FileName);
                xmlWriter.Close();
            }
        }

        private void m_cmbLevel_SelectedIndexChanged(object p_objSender, EventArgs p_eventArgs)
        {
            m_nSelectedLevel = m_cmbLevel.SelectedIndex;
            Level level = m_world.Levels[m_nSelectedLevel];

            m_nSelectedLayer = level.Layers.Count > 0 ? 0 : -1;
            UpdateUILayerList();

            UpdateUIButtonState();
            UpdateUIScrollBars();

            m_tscMain.ContentPanel.Invalidate();
        }

        private void m_btnLevelNew_Click(object p_objSender, EventArgs p_eventArgs)
        {
            Level level = new Level();
            level.Name = NameManager.GenerateLevelName(m_world);
            Layer layer = new Layer();
            layer.Name = NameManager.GenerateLayerName(level);
            level.Layers.Add(layer);

            LevelPropertiesForm levelPropertiesForm = new LevelPropertiesForm(level);
            if (levelPropertiesForm.ShowDialog(this) == DialogResult.OK)
            {
                m_world.Levels.Add(level);
                m_nSelectedLevel = m_world.Levels.Count - 1;
                UpdateUI();
            }
        }

        private void m_btnLevelProperties_Click(object p_objSender, EventArgs p_eventArgs)
        {
            Level level = m_world.Levels[m_nSelectedLevel];
            LevelPropertiesForm levelPropertiesForm = new LevelPropertiesForm(level);
            if (levelPropertiesForm.ShowDialog(this) == DialogResult.OK)
                UpdateUI();
        }

        private void m_btnLevelDelete_Click(object p_objSender, EventArgs p_eventArgs)
        {
            if (MessageBox.Show(
                "Discard the existing level?", "Delete level",
                MessageBoxButtons.YesNo, MessageBoxIcon.Question,
                MessageBoxDefaultButton.Button2) == DialogResult.Yes)
            {
                m_world.Levels.RemoveAt(m_nSelectedLevel);
                --m_nSelectedLevel;
                UpdateUI();
            }
        }

        private void m_cmbLayer_SelectedIndexChanged(object p_objSender, EventArgs p_eventArgs)
        {
            m_nSelectedLayer = m_cmbLayer.SelectedIndex;
            m_tscMain.ContentPanel.Invalidate();
        }

        private void m_btnLayerNew_Click(object p_objSender, EventArgs p_eventArgs)
        {
            Level level = m_world.Levels[m_nSelectedLevel];
            Layer layer = new Layer();
            layer.Name = NameManager.GenerateLayerName(level);
            LayerPropertiesForm layerPropertiesForm = new LayerPropertiesForm(level, layer);
            if (layerPropertiesForm.ShowDialog(this) == DialogResult.OK)
            {
                level.Layers.Add(layer);
                m_nSelectedLayer = level.Layers.Count - 1;
                UpdateUILayerList();
            }
        }

        private void m_btnLayerProperties_Click(object p_objSender, EventArgs p_eventArgs)
        {
            Level level = m_world.Levels[m_nSelectedLevel];
            Layer layer = level.Layers[m_nSelectedLayer];
            LayerPropertiesForm layerPropertiesForm = new LayerPropertiesForm(level, layer);
            if (layerPropertiesForm.ShowDialog(this) == DialogResult.OK)
                m_tscMain.ContentPanel.Invalidate();
        }

        private void m_btnLayerMove_Click(object p_objSender, EventArgs p_eventArgs)
        {
            m_editTool = EditTool.LayerMove;
            m_tscMain.ContentPanel.Cursor = Cursors.SizeAll;
            UpdateUIButtonState();
        }

        private void m_btnLayerRotate_Click(object p_objSender, EventArgs p_eventArgs)
        {
            m_editTool = EditTool.LayerRotate;
            m_tscMain.ContentPanel.Cursor = new Cursor(Properties.Resources.CursorRotate.GetHicon());
            UpdateUIButtonState();
        }

        private void m_btnLayerDelete_Click(object p_objSender, EventArgs p_eventArgs)
        {
            Level level = m_world.Levels[m_nSelectedLevel];
            Layer layer = level.Layers[m_nSelectedLayer];
            if (MessageBox.Show("Delete layer '" + layer.Name + "'?", "Delete layer",
                MessageBoxButtons.YesNo, MessageBoxIcon.Question,
                MessageBoxDefaultButton.Button2) == DialogResult.Yes)
            {
                level.Layers.RemoveAt(m_nSelectedLayer);
                m_nSelectedLayer = Math.Min(m_nSelectedLayer, level.Layers.Count - 1);
                UpdateUIButtonState();
                UpdateUILayerList();
                m_tscMain.ContentPanel.Invalidate();
            }
        }

        private void m_btnDrawDrawEdge_Click(object p_objSender, EventArgs p_eventArgs)
        {
            m_editTool = EditTool.EdgeDraw;
            m_tscMain.ContentPanel.Cursor = Cursors.Cross;
            UpdateUIButtonState();
        }

        private void m_btnDrawDragVertex_Click(object p_objSender, EventArgs p_eventArgs)
        {
            m_editTool = EditTool.VertexMove;
            m_tscMain.ContentPanel.Cursor = Cursors.SizeAll;
            UpdateUIButtonState();
        }

        private void m_btnDrawDeleteEdge_Click(object p_objSender, EventArgs p_eventArgs)
        {
            m_editTool = EditTool.EdgeDelete;
            m_tscMain.ContentPanel.Cursor = new Cursor(Properties.Resources.CursorDelete.GetHicon());
            UpdateUIButtonState();
        }

        private void m_btnDrawColourEdge_Click(object p_objSender, EventArgs p_eventArgs)
        {
            m_editTool = EditTool.EdgeColour;
            m_tscMain.ContentPanel.Cursor = new Cursor(Properties.Resources.CursorPaint.GetHicon());
            UpdateUIButtonState();
        }

        private void m_btnDrawChooseColour_Click(object p_objSender, EventArgs p_eventArgs)
        {
            if (m_colorDialog.ShowDialog(this) == DialogResult.OK)
            {
                m_colorEdge = m_colorDialog.Color;
                m_btnDrawChooseColour.Image = CreateColourBitmap(m_colorEdge);
            }
        }

        private void m_cmbEntity_SelectedIndexChanged(object p_objSender, EventArgs p_eventArgs)
        {

        }

        private void m_btnEntityNew_Click(object p_objSender, EventArgs p_eventArgs)
        {
            m_editTool = EditTool.EntityNew;
            m_tscMain.ContentPanel.Cursor = new Cursor(Properties.Resources.CursorEntity.GetHicon());
            UpdateUIButtonState();
        }

        private void m_btnEntityProperties_Click(object p_objSender, EventArgs p_eventArgs)
        {
            m_editTool = EditTool.EntityProperties;
            m_tscMain.ContentPanel.Cursor = Cursors.Default;
            UpdateUIButtonState();
        }

        private void m_btnEntityMove_Click(object p_objSender, EventArgs p_eventArgs)
        {
            m_editTool = EditTool.EntityMove;
            m_tscMain.ContentPanel.Cursor = Cursors.SizeAll;
            UpdateUIButtonState();
        }

        private void m_btnEntityRotate_Click(object p_objSender, EventArgs p_eventArgs)
        {
            m_editTool = EditTool.EntityRotate;
            m_tscMain.ContentPanel.Cursor = new Cursor(Properties.Resources.CursorRotate.GetHicon());
            UpdateUIButtonState();
        }

        private void m_btnEntityDelete_Click(object p_objSender, EventArgs p_eventArgs)
        {
            m_editTool = EditTool.EntityDelete;
            m_tscMain.ContentPanel.Cursor = new Cursor(Properties.Resources.CursorDelete.GetHicon());
            UpdateUIButtonState();
        }

        private void m_hsbMain_Scroll(object p_objSender, ScrollEventArgs p_scrollEventArgs)
        {
            m_vecViewportPosition.X = p_scrollEventArgs.NewValue;
            m_tscMain.ContentPanel.Invalidate();
        }

        private void m_vsbMain_Scroll(object p_objSender, ScrollEventArgs p_scrollEventArgs)
        {
            m_vecViewportPosition.Y = p_scrollEventArgs.NewValue;
            m_tscMain.ContentPanel.Invalidate();
        }

        private void m_tscMain_ContentPanel_MouseDown(object p_objSender, MouseEventArgs p_mouseEventArgs)
        {
            if (p_mouseEventArgs.Button != MouseButtons.Left)
                return;

            m_vecMousePosition = new Vector2(
                p_mouseEventArgs.X + m_hsbMain.Value,
                p_mouseEventArgs.Y + m_vsbMain.Value);

            m_bDragging = true;
            Level level = m_world.Levels[m_nSelectedLevel];
            Layer layer = level.Layers[m_nSelectedLayer];

            switch (m_editTool)
            {
                case EditTool.LayerMove:
                    m_vecDragStart = m_vecMousePosition;
                    m_vecOldPosition = layer.Position;
                    break;
                case EditTool.LayerRotate:
                    m_vecDragStart = m_vecMousePosition;
                    m_fOldOrientation = layer.Orientation;
                    break;
                case EditTool.EdgeDraw:
                    List<PickedVertex> listPickedVertices = level.PickVertices(m_vecMousePosition);
                    if (listPickedVertices.Count > 0)
                        m_vecDragStart = listPickedVertices[0].Vertex;
                    else
                        m_vecDragStart = m_vecMousePosition;
                    m_vecDragEnd = m_vecDragStart;
                    break;
                case EditTool.VertexMove:
                    m_listPickedVertices = level.PickVertices(m_vecMousePosition);
                    break;
                case EditTool.EntityNew:
                    EntityMarker entityMarker = new EntityMarker();
                    entityMarker.Name = NameManager.GenerateEntityName(level);
                    entityMarker.EntityType = (EntityType) Enum.Parse(
                        typeof(EntityType), m_cmbEntity.SelectedItem.ToString());
                    entityMarker.Position = m_vecMousePosition;

                    EntityPropertiesForm entityPropertiesForm
                        = new EntityPropertiesForm(level, entityMarker);
                    if (entityPropertiesForm.ShowDialog(this) == DialogResult.OK)
                    {
                        level.EntityMarkers.Add(entityMarker);
                        m_bDragging = false;
                        m_tscMain.ContentPanel.Invalidate();
                    }
                    break;
                case EditTool.EntityProperties:
                    m_entityMarkerSelected = level.PickEntityMarker(m_vecMousePosition);
                    if (m_entityMarkerSelected != null)
                    {
                        entityPropertiesForm
                            = new EntityPropertiesForm(level, m_entityMarkerSelected);
                        entityPropertiesForm.ShowDialog(this);
                        m_entityMarkerSelected = null;
                        m_bDragging = false;
                        m_tscMain.ContentPanel.Invalidate();
                    }
                    break;
                case EditTool.EntityMove:
                    m_vecDragStart = m_vecMousePosition;
                    m_entityMarkerSelected = level.PickEntityMarker(m_vecMousePosition);
                    if (m_entityMarkerSelected != null)
                        m_vecOldPosition = m_entityMarkerSelected.Position;
                    break;
                case EditTool.EntityRotate:
                    m_vecDragStart = m_vecMousePosition;
                    m_entityMarkerSelected = level.PickEntityMarker(m_vecMousePosition);
                    if (m_entityMarkerSelected != null)
                        m_fOldOrientation = m_entityMarkerSelected.Orientation;
                    break;
            }
        }

        private void m_tscMain_ContentPanel_MouseMove(object p_objSender, MouseEventArgs p_mouseEventArgs)
        {
            Level level = m_world.Levels[m_nSelectedLevel];
            Layer layer = level.Layers[m_nSelectedLayer];

            m_vecMousePosition = new Vector2(
                p_mouseEventArgs.X + m_hsbMain.Value,
                p_mouseEventArgs.Y + m_vsbMain.Value);

            if (m_bDragging)
            {
                switch (m_editTool)
                {
                    case EditTool.LayerMove:
                        layer.Position = m_vecOldPosition + (m_vecMousePosition - m_vecDragStart);
                        break;
                    case EditTool.LayerRotate:
                        Vector2 vecDirection1 = m_vecDragStart - layer.Position;
                        Vector2 vecDirection2 = m_vecMousePosition - layer.Position;
                        float fOrientation1 = (float)Math.Atan2(vecDirection1.Y, vecDirection1.X);
                        float fOrientation2 = (float)Math.Atan2(vecDirection2.Y, vecDirection2.X);
                        float fDeltaOrientation = fOrientation2 - fOrientation1;
                        layer.Orientation = m_fOldOrientation + fDeltaOrientation;
                        break;
                    case EditTool.EdgeDraw:
                        List<PickedVertex> listPickedVertices = level.PickVertices(m_vecMousePosition);
                        if (listPickedVertices.Count > 0)
                            m_vecDragEnd = listPickedVertices[0].Vertex;
                        else
                            m_vecDragEnd = m_vecMousePosition;
                        break;
                    case EditTool.VertexMove:
                        foreach (PickedVertex pickedVertex in m_listPickedVertices)
                            pickedVertex.Vertex = m_vecMousePosition;
                        break;
                    case EditTool.EdgeColour:
                        m_listPickedEdges = level.PickEdges(m_vecMousePosition);
                        Microsoft.Xna.Framework.Graphics.Color colourNew
                            = new Microsoft.Xna.Framework.Graphics.Color(
                                m_colorEdge.R, m_colorEdge.G, m_colorEdge.B);
                        foreach (PickedEdge pickedEdge in m_listPickedEdges)
                            pickedEdge.LocalEdge.Colour = colourNew;
                        if (m_listPickedEdges.Count > 0)
                            m_tscMain.ContentPanel.Invalidate();
                        break;
                    case EditTool.EntityMove:
                        if (m_entityMarkerSelected != null)
                            m_entityMarkerSelected.Position = m_vecOldPosition + (m_vecMousePosition - m_vecDragStart);
                        break;
                    case EditTool.EntityRotate:
                        if (m_entityMarkerSelected != null)
                        {
                            vecDirection1 = m_vecDragStart - m_entityMarkerSelected.Position;
                            vecDirection2 = m_vecMousePosition - m_entityMarkerSelected.Position;
                            fOrientation1 = (float)Math.Atan2(vecDirection1.Y, vecDirection1.X);
                            fOrientation2 = (float)Math.Atan2(vecDirection2.Y, vecDirection2.X);
                            fDeltaOrientation = fOrientation2 - fOrientation1;
                            m_entityMarkerSelected.Orientation = m_fOldOrientation + fDeltaOrientation;
                        }
                        break;
                }
                m_tscMain.ContentPanel.Invalidate();
            }
            else
            {
                switch (m_editTool)
                {
                    case EditTool.VertexMove:
                        List<PickedVertex> listPickedVertices = level.PickVertices(m_vecMousePosition);
                        if (listPickedVertices.Count != m_listPickedVertices.Count)
                            m_tscMain.ContentPanel.Invalidate();
                        m_listPickedVertices = listPickedVertices;
                        break;
                    case EditTool.EdgeDelete:
                    case EditTool.EdgeColour:
                        List<PickedEdge> listPickedEdges = level.PickEdges(m_vecMousePosition);
                        if (listPickedEdges.Count != m_listPickedEdges.Count)
                            m_tscMain.ContentPanel.Invalidate();
                        m_listPickedEdges = listPickedEdges;
                        break;
                    case EditTool.EntityProperties:
                    case EditTool.EntityMove:
                    case EditTool.EntityRotate:
                    case EditTool.EntityDelete:
                        EntityMarker entityMarker = level.PickEntityMarker(m_vecMousePosition);
                        if (m_entityMarkerSelected != entityMarker)
                            m_tscMain.ContentPanel.Invalidate();
                        m_entityMarkerSelected = entityMarker;
                        break;
                }
            }
        }

        private void m_tscMain_ContentPanel_MouseUp(object p_objSender, MouseEventArgs p_mouseEventArgs)
        {
            if (p_mouseEventArgs.Button != MouseButtons.Left)
                return;

            Level level = m_world.Levels[m_nSelectedLevel];
            Layer layer = level.Layers[m_nSelectedLayer];

            m_bDragging = false;

            switch (m_editTool)
            {
                case EditTool.EdgeDraw:
                    if (m_vecDragStart != m_vecDragEnd)
                    {
                        List<Edge> listEdges = layer.Edges;
                        Edge edgeNew = new Edge(m_vecDragStart, m_vecDragEnd, ConvertGDIToXNAColour(m_colorEdge));
                        edgeNew.Transform(layer.Placement.Inverse);
                        listEdges.Add(edgeNew);
                        layer.Edges = listEdges;
                        UpdateUIButtonState();
                        m_tscMain.ContentPanel.Invalidate();
                    }
                    break;
                case EditTool.EdgeDelete:
                    m_listPickedEdges = level.PickEdges(m_vecMousePosition);
                    foreach (PickedEdge pickedEdge in m_listPickedEdges)
                        pickedEdge.Layer.Edges.Remove(pickedEdge.LocalEdge);
                    UpdateUIButtonState();
                    m_tscMain.ContentPanel.Invalidate();
                    break;
                case EditTool.EntityDelete:
                    EntityMarker entityMarker = level.PickEntityMarker(m_vecMousePosition);
                    if (entityMarker != null)
                    {
                        level.EntityMarkers.Remove(entityMarker);
                        m_tscMain.ContentPanel.Invalidate();
                    }
                    break;
            }
        }

        private void m_tscMain_ContentPanel_Paint(object p_objSender, PaintEventArgs p_paintEventArgs)
        {
            if (m_nSelectedLevel < 0)
                return;

            Graphics graphics = p_paintEventArgs.Graphics;
            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            // set transform according to viewport
            System.Drawing.Drawing2D.Matrix matTransform = new System.Drawing.Drawing2D.Matrix();
            matTransform.Translate(-m_vecViewportPosition.X, -m_vecViewportPosition.Y);
            graphics.Transform = matTransform;

            //graphics.Clear(System.Drawing.Color.Black);

            // get selected level
            Level level = m_world.Levels[m_nSelectedLevel];

            // draw level boundaries
            Pen penDimensions = new Pen(System.Drawing.Color.DimGray);
            penDimensions.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
            graphics.DrawLine(penDimensions, level.Dimensions.X, 0.0f, level.Dimensions.X, level.Dimensions.Y);
            graphics.DrawLine(penDimensions, 0.0f, level.Dimensions.Y, level.Dimensions.X, level.Dimensions.Y);

            // process all layers
            foreach (Layer layer in level.Layers)
            {
                // draw layer origin
                DrawOrigin(graphics, layer.Placement, System.Drawing.Color.Gray);

                // draw edges
                foreach (Edge edge in layer.Edges)
                {
                    Pen pen = new Pen(ConvertXNAToGDIColour(edge.Colour), 2.0f);
                    Edge edgeTransformed = new Edge(edge);
                    edgeTransformed.Transform(layer.Placement);
                    graphics.DrawLine(pen,
                        edgeTransformed.Start.X, edgeTransformed.Start.Y,
                        edgeTransformed.End.X, edgeTransformed.End.Y);

                    Vector2 vecMidpoint = edgeTransformed.Midpoint;
                    Vector2 vecNormalEnd = vecMidpoint + edgeTransformed.Normal * 4.0f;
                    graphics.DrawLine(pen,
                        vecMidpoint.X, vecMidpoint.Y,
                        vecNormalEnd.X, vecNormalEnd.Y);
                }
            }

            // process entity markers
            foreach (EntityMarker entityMarker in level.EntityMarkers)
            {
                DrawOrigin(graphics, entityMarker.Placement, System.Drawing.Color.White);
                PrintText(graphics,
                    entityMarker.EntityType.ToString() + ": " + entityMarker.Name,
                    entityMarker.Position, System.Drawing.Color.White);
            }

            if (m_bDragging)
            {
                switch (m_editTool)
                {
                    case EditTool.EdgeDraw:
                        Pen pen = new Pen(m_colorEdge);
                        graphics.DrawLine(pen, m_vecDragStart.X, m_vecDragStart.Y, m_vecDragEnd.X, m_vecDragEnd.Y);
                        break;
                }
            }
            else
            {
                // highlighting
                switch (m_editTool)
                {
                    case EditTool.VertexMove:
                        foreach (PickedVertex pickedVertex in m_listPickedVertices)
                            graphics.DrawEllipse(Pens.White, pickedVertex.Vertex.X - 10.0f, pickedVertex.Vertex.Y - 10.0f, 20.0f, 20.0f);
                        break;
                    case EditTool.EdgeDelete:
                        Pen penDeleteEdge = new Pen(System.Drawing.Color.FromArgb(128, 255, 0, 0), 8.0f);
                        foreach (PickedEdge pickedEdge in m_listPickedEdges)
                            graphics.DrawLine(penDeleteEdge, pickedEdge.Edge.Start.X, pickedEdge.Edge.Start.Y,
                                pickedEdge.Edge.End.X, pickedEdge.Edge.End.Y);
                        break;
                    case EditTool.EdgeColour:
                        Pen penColourEdge = new Pen(m_colorEdge, 3.0f);
                        foreach (PickedEdge pickedEdge in m_listPickedEdges)
                            graphics.DrawLine(penColourEdge, pickedEdge.Edge.Start.X, pickedEdge.Edge.Start.Y,
                                pickedEdge.Edge.End.X, pickedEdge.Edge.End.Y);
                        break;
                    case EditTool.EntityProperties:
                    case EditTool.EntityMove:
                    case EditTool.EntityRotate:
                    case EditTool.EntityDelete:
                        foreach (EntityMarker entityMarker in level.PickEntityMarkers(m_vecMousePosition))
                            DrawOrigin(graphics, entityMarker.Placement, System.Drawing.Color.Yellow);
                        break;
                }
            }
        }

        public MainForm()
        {
            InitializeComponent();
        }
    }
}